package com.zz.framework.core.ext

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.viewbinding.ViewBinding

/**
 * Copyright © 2019 zanezhao All rights reserved.
 * Created by zanezhao on 2019-12-01 12:21.
 * @author: zanezhao 1820022519@qq.com
 * @version: V1.0
 */
fun <T : ViewDataBinding> FragmentActivity.dataBinding(@LayoutRes layoutResId: Int): Lazy<T> {
    return object : Lazy<T> {
        private var binding: T? = null
        override fun isInitialized(): Boolean = binding != null
        override val value: T
            get() = binding ?: setContentView<T>(this@dataBinding, layoutResId).also {
                binding = it
            }
    }
}

fun <T : ViewDataBinding> setContentView(activity: FragmentActivity, layoutResId: Int): T {
    return DataBindingUtil.inflate<T>(activity.layoutInflater, layoutResId, null, false).also {
        // On Android 8, DataBindingUtil.setContentView will crash sometimes.
        // https://stackoverflow.com/questions/50060653/app-crashing-when-using-android-data-binding-on-android-8
        activity.setContentView(it.root)
        it.lifecycleOwner = activity
    }
}

fun <T : ViewDataBinding> Fragment.dataBinding(@LayoutRes layoutResId: Int): Lazy<T> {
    return object : Lazy<T> {
        private var binding: T? = null
        override fun isInitialized(): Boolean = binding != null
        override val value: T
            get() = binding ?: inflate<T>(this@dataBinding, layoutResId).also {
                binding = it
                viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
                    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                    fun onDestroyView() {
                        viewLifecycleOwner.lifecycle.removeObserver(this)
                        binding = null // For Fragment's view recreation
                    }
                })
            }
    }
}

@JvmOverloads
fun <T : ViewDataBinding> inflate(
    fragment: Fragment,
    layoutResId: Int,
    container: ViewGroup? = fragment.requireActivity().findViewById(fragment.id) as? ViewGroup,
    attachToParent: Boolean = false
): T {
    return DataBindingUtil.inflate<T>(fragment.layoutInflater, layoutResId, container, attachToParent).also {
        it.lifecycleOwner = fragment.viewLifecycleOwner
    }
}

inline fun <reified T : ViewBinding> FragmentActivity.viewBinding(
    noinline inflate: (inflater: LayoutInflater) -> T = {
        T::class.java.getMethod("inflate").invoke(layoutInflater) as T
    }
): Lazy<T> {
    return object : Lazy<T> {
        private var binding: T? = null
        override fun isInitialized(): Boolean = binding != null
        override val value: T
            get() = binding ?: setContentView(inflate).also {
                binding = it
            }
    }
}

fun <T : ViewBinding> FragmentActivity.setContentView(inflate: (inflater: LayoutInflater) -> T): T {
    return inflate(layoutInflater).also {
        setContentView(it.root)
    }
}

inline fun <reified T : ViewBinding> Fragment.viewBinding(
    crossinline inflate: (inflater: LayoutInflater, container: ViewGroup?) -> T = { inflater: LayoutInflater, container: ViewGroup? ->
        T::class.java.getMethod("inflate").invoke(inflater, container, false) as T
    }
): Lazy<T> {
    return object : Lazy<T> {
        private var binding: T? = null
        override fun isInitialized(): Boolean = binding != null
        override val value: T
            get() = binding ?: inflate(
                layoutInflater,
                requireActivity().findViewById(id) as? ViewGroup
            ).also {
                binding = it
                viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
                    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                    fun onDestroyView() {
                        viewLifecycleOwner.lifecycle.removeObserver(this)
                        binding = null // For Fragment's view recreation
                    }
                })
            }
    }
}